#include "memoryitemmodel.h"
#include "memory.h"
#include "breakpointtable.h"
#include "utilities.h"
#include "machine.h"
#include "cpu.h"
#include "memorywidget.h"

#include <QIcon>
#include <QTableView>
#include <iostream>


using namespace std;

const quint32 MemoryItemModel::NUMBER_PER_PAGE = 16384;
const quint32 MemoryItemModel::NUMBER_OF_PAGE = 65536;

MemoryItemModel::MemoryItemModel(MemoryWidget* _widget, QObject *parent)
    : QAbstractTableModel(parent), widget(_widget),
    memory(Memory::getInstance()), cpu(CPU::getInstance()),breakTable(BreakPointTable::getInstance()),
    baseAddress(0), pageIndex(0), lastPCIndex(0)
{
    connect(memory, SIGNAL(dataChanged(quint32)), this, SLOT(updateMemory(quint32)));
    connect(cpu, SIGNAL(registersChanged(int)), this, SLOT(updatePC(int)));
}

MemoryItemModel::~MemoryItemModel()
{
}

void MemoryItemModel::updateMemory(quint32 address)
{
    if(address >= baseAddress && address <= baseAddress + NUMBER_PER_PAGE * 4)
    {
        int row = (address - baseAddress) / 4;
        int column = (address - baseAddress) % 4;
        emit dataChanged(index(row, column + 2), index(row, column + 2));
    }
}

void MemoryItemModel::changeBreakPoint(const QModelIndex &_index)
{
    if(_index.column() != 0)
    {
        return;
    }
    int address = _index.row() * 4 + baseAddress;
    if(breakTable->containsBreakPoint(address))
    {
        breakTable->removeBreakPont(address);
    }
    else
    {
        breakTable->addBreakPoint(address);
    }
    emit dataChanged(index(_index.row(), 0), index(_index.row(), 0));
}

void MemoryItemModel::setPageIndex(quint32 _index)
{
    if(_index >= NUMBER_OF_PAGE)
    {
        return;
    }
    pageIndex = _index;
    baseAddress = NUMBER_PER_PAGE * _index * 4;
    emit dataChanged(index(0, 0), index(NUMBER_PER_PAGE, 6));
}

quint32 MemoryItemModel::getBaseAddress() const
{
    return baseAddress;
}

void MemoryItemModel::updatePC(int id)
{
    if(id == CPU::ID_PC)
    {
        cout<<"OK"<<endl;
        emit dataChanged(index(lastPCIndex, 0), index(lastPCIndex, 0));
        quint32 address = cpu->getPC();
        widget->goToAddress(address);
        quint32 pc_index = (address - baseAddress) / 4;
        emit dataChanged(index(pc_index, 0), index(pc_index, 0));
        lastPCIndex = pc_index;
    }
}

int MemoryItemModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return NUMBER_PER_PAGE;
}

int MemoryItemModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return 7;
}

Qt::ItemFlags MemoryItemModel::flags(const QModelIndex &index) const
{
    if(index.isValid() && index.column() >= 2 && index.column() <= 5)
    {
        return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
    }
    else
    {
        return QAbstractTableModel::flags(index);
    }
}

bool MemoryItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole && value.toString().size() == 8) {
        quint32 address = baseAddress + index.row() * 4 + index.column() - 2;
        memory->write(address, value.toString());
        emit dataChanged(index, index);
        return true;
    }
    return false;
}

QVariant MemoryItemModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
    {
        return QVariant();
    }
    else if (index.row() >= rowCount(index) || index.column() >= columnCount(index))
    {
        return QVariant();
    }
    switch(index.column())
    {
    case 0:
        {
            if(role == Qt::DecorationRole)
            {
                quint32 address = index.row() * 4 + baseAddress;
                if(address == cpu->getPC())
                {
                    return QIcon(QString(":/images/pointer.png"));
                }
                if(breakTable->containsBreakPoint(address))
                {
                    return QIcon(QString(":/images/break_point.png"));
                }
                else
                {
                    return QVariant();
                }
            }
            else
            {
                return QVariant();
            }
        }
    case 1:
        {
            if(role == Qt::DisplayRole)
            {
                quint32 address = index.row() * 4 + baseAddress;
                return Utilities::uintToHex(address);
            }
            else
            {
                return QVariant();
            }
        }
    case 2:
    case 3:
    case 4:
    case 5:
        {
            if(role == Qt::DisplayRole)
            {
                quint32 address = index.row() * 4 + baseAddress;
                int counter = index.column() - 2;
                return memory->read(address + counter);
            }
            else
            {
                return QVariant();
            }
        }
    case 6:
        {
            if(role == Qt::DisplayRole)
            {
                int row = index.row();
                quint32 address = row * 4 + baseAddress;
                QString content;
                for(int i = 0; i < 4; i++)
                {
                    content.append(memory->read(address + i));
                }
                content = Utilities::binToHex(content);
                return QVariant(content);
            }
            else
            {
                return QVariant();
            }
        }
    }
    return QVariant();
}


